import { Scene } from '@babylonjs/core/scene';
import { AbstractSelectConfig } from './config/abstract-select-config';
import { ISelectInfo, SelectPick } from './select-pick';
import { CreateHandleTo, IHandle, RemoveHandle } from '../../data/data-handle';


/**
 * 选择模式
 * @param SINGLE 单选
 * @param MULTIPLE 多选
 */
enum SELECT_MODE {
    SINGLE, MULTIPLE
}

/**
 * 选择类型
 * @param CLICK_PICK 单击选择
 * @param DOUBLE_CLICK_PICK 双击选择
 * @param NONE 无
 */
enum SELECT_STYLE {
    CLICK_PICK, DOUBLE_CLICK_PICK, NONE
}

/**
 * 选择器参数对象
 * @param mode 选择模式
 * @param style 选择类型
 * @param clearOnEmpty 点击空白时是否清空选择
 * @param unselectOnRepeat 重复选择时是否取消选择
 * @param doNotClearScenePickable 是否不清空场景其它网格原本的可拾取属性
 * @param clickDitherThreshold 点击时的抖动阈值
 */
interface ISelectorOptions {
    mode?: SELECT_MODE,
    style?: SELECT_STYLE,
    clearOnEmpty?: boolean,
    unselectOnRepeat?: boolean,
    doNotClearScenePickable?: boolean,
    clickDitherThreshold?: number,
}


/**
 * 选择器
 * @param scene 所在场景
 * @param options 参数对象
 */
class Selector {

    private _mode: SELECT_MODE;
    private _style: SELECT_STYLE;
    private _configs: AbstractSelectConfig<any>[] = [];
    private _targets: any[] = [];
    private _selectedTargets: any[] = [];
    private _selectedConfigs: AbstractSelectConfig<any>[] = [];
    private _pick: SelectPick;
    private _selectHandles: IHandle<any>[] = [];
    private _unselectHandles: IHandle<any>[] = [];
    private _blocking = false;
    private _active = true;

    /**
     * 所在场景
     */
    public readonly scene: Scene;

    /**
     * 禁用选择改变时的回调函数调用
     */
    public noChangeHandle: boolean = false;

    /**
     * 忽略下一次拾取操作
     */
    public ignoreNextPick: boolean = false;

    constructor(scene: Scene, options?: ISelectorOptions) {
        this.scene = scene;
        const ops = options || {};
        this._mode = ops.mode === undefined ? SELECT_MODE.MULTIPLE : ops.mode;
        this._style = ops.style === undefined ? SELECT_STYLE.CLICK_PICK : ops.style;
        const clearOnEmpty = ops.clearOnEmpty === undefined ? true : ops.clearOnEmpty;
        const unselectOnRepeat = ops.unselectOnRepeat === undefined ? true : ops.unselectOnRepeat;

        this._pick = new SelectPick(scene, {
            useClick: this._style === SELECT_STYLE.CLICK_PICK,
            useDoubleClick: this._style === SELECT_STYLE.DOUBLE_CLICK_PICK,
            defaultPickable: ops.doNotClearScenePickable ? undefined : false,
            clickThreshold: ops.clickDitherThreshold || 1,
        });
        this._pick.onSelect((selectInfo: ISelectInfo | null) => {
            if (this.ignoreNextPick) {
                this.ignoreNextPick = false;
                return;
            }
            if (!selectInfo) {
                if (!clearOnEmpty) return;
                this.clearSelected();
            } else {
                const target = selectInfo.target;
                const idx = Selector.getTargetIndex(target, this._selectedTargets, this._selectedConfigs);
                if (idx < 0) {
                    this.select(target);
                } else {
                    if (unselectOnRepeat) this.unselect(target);
                }
            }
        });
    }

    /**
     * 设置是否启用
     */
    public set activated(activated: boolean) {
        this._active = activated;
        this._pick.isEnabled = activated;
    }

    /**
     * 获取是否启用
     */
    public get activated(): boolean {
        return this._active;
    }

    /**
     * 获取所有配置
     */
    public get configs(): AbstractSelectConfig<any>[] {
        return this._configs;
    }

    /**
     * 获取所有选择对象
     */
    public get targets(): any[] {
        return this._targets;
    }

    /**
     * 获取已被选择的配置
     */
    public get selectedConfigs(): AbstractSelectConfig<any>[] {
        return this._selectedConfigs;
    }

    /**
     * 获取已被选择的对象
     */
    public get selectedTargets(): any[] {
        return this._selectedTargets;
    }

    /**
     * 添加选择时的回调函数对象
     * @param func 回调函数
     * @returns 回调函数对象
     */
    public onSelect(func: { (targets: any[], configs: AbstractSelectConfig<any>[]): void }): IHandle<any> {
        return CreateHandleTo(func, this._selectHandles);
    }

    /**
     * 移除选择时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeSelectHandle(handle: number | IHandle<any>): void {
        RemoveHandle(handle, this._selectHandles);
    }

    /**
     * 添加取消选择时的回调函数对象
     * @param func 回调函数
     * @returns 回调函数对象
     */
    public onUnselect(func: { (targets: any[], configs: AbstractSelectConfig<any>[]): void }): IHandle<any> {
        return CreateHandleTo(func, this._unselectHandles);
    }

    /**
     * 移除取消选择时的回调函数对象
     * @param handle 回调函数对象或其id
     */
    public removeUnselectHandle(handle: number | IHandle<any>): void {
        RemoveHandle(handle, this._unselectHandles);
    }

    /**
     * 获取选择对象的配置
     * @param target 选择对象
     * @returns 选择配置
     */
    public configOfTarget(target: any): AbstractSelectConfig<any> | null {
        const idx = Selector.getTargetIndex(target, this._targets, this._configs);
        if (idx < 0) return null;
        return this._configs[idx];
    }

    /**
     * 添加可选择对象
     * @param target 目标对象
     * @param config 选择配置
     * @returns 是否添加成功
     */
    public addTarget(target: any, config: AbstractSelectConfig<any>): boolean {
        if (Selector.getTargetIndex(target, this._targets, this._configs) >= 0) return true;
        if (!config.test(target)) return false;
        this._pick.add({ target: target, config: config });
        this._configs.push(config);
        this._targets.push(target);
        return true;
    }

    /**
     * 移除可选择对象
     * @param target 目标对象
     */
    public removeTarget(target: any): void {
        let idx = Selector.getTargetIndex(target, this._targets, this._configs);
        if (idx < 0) return;
        this.unselect(this._targets[idx]);
        this._pick.remove({ target: this._targets[idx], config: this._configs[idx] });
        this._targets.splice(idx, 1);
        this._configs.splice(idx, 1);
    }

    /**
     * 清除可选择对象
     */
    public clear(): void {
        this.clearSelected();
        this._pick.clear();
        this._targets.length = 0;
        this._configs.length = 0;
    }

    /**
     * 选择目标对象
     * @param target 目标对象
     * @returns 相应的配置
     */
    public select(target: any): AbstractSelectConfig<any> | null {
        if (Selector.getTargetIndex(target, this._selectedTargets, this._selectedConfigs) >= 0) return null;

        const idx = Selector.getTargetIndex(target, this._targets, this._configs);
        if (idx < 0) return null;
        if (this._mode === SELECT_MODE.SINGLE) this.clearSelected();
        const cnf = this._configs[idx];
        this._selectedTargets.push(target);
        this._selectedConfigs.push(cnf);
        cnf.setSelected(target);
        if ((!this._blocking) && (!this.noChangeHandle)) this._selectHandles.forEach(handle => handle.callback([target], [cnf]));
        return cnf;
    }

    /**
     * 选择多个目标对象
     * @param targets 目标对象集合
     * @returns 相应的配置集合
     */
    public selectMultiple(...targets: any[]): AbstractSelectConfig<any>[] {
        const cnfs: AbstractSelectConfig<any>[] = [];
        if (!targets.length) return cnfs;
        const tars: any[] = [];
        if (this._mode === SELECT_MODE.SINGLE) {
            const cnf = this.select(targets[0]);
            if (cnf) cnfs.push(cnf);
            return cnfs;
        }
        this._blocking = true;
        targets.forEach(tar => {
            const cnf = this.select(tar);
            if (cnf) {
                cnfs.push(cnf);
                tars.push(tar);
            }
        });
        this._blocking = false;
        if (!this.noChangeHandle) this._selectHandles.forEach(handle => handle.callback(tars, cnfs));
        return cnfs;
    }

    /**
     * 取消选择
     * @param target 目标对象
     * @returns 相应的配置
     */
    public unselect(target: any): AbstractSelectConfig<any> | null {
        const idx = Selector.getTargetIndex(target, this._selectedTargets, this._selectedConfigs);
        if (idx < 0) return null;
        const cnf = this._selectedConfigs[idx];
        this._selectedTargets.splice(idx, 1);
        this._selectedConfigs.splice(idx, 1);
        cnf.setUnselected(target);
        if ((!this._blocking) && (!this.noChangeHandle)) this._unselectHandles.forEach(handle => handle.callback([target], [cnf]));
        return cnf;
    }

    /**
     * 取消选择多个
     * @param targets 要取消选择的对象
     * @returns 相应的配置集合
     */
    public unselectMultiple(...targets: any[]): AbstractSelectConfig<any>[] {
        const cnfs: AbstractSelectConfig<any>[] = [];
        if (!targets.length) return cnfs;
        const tars: any[] = [];
        this._blocking = true;
        targets.forEach(tar => {
            const cnf = this.unselect(tar);
            if (cnf) {
                tars.push(tar);
                cnfs.push(cnf);
            }
        });
        this._blocking = false;
        if (!this.noChangeHandle) this._unselectHandles.forEach(handle => handle.callback(tars, cnfs));
        return cnfs;
    }

    /**
     * 清除选择
     * @returns 相应的配置集合 
     */
    public clearSelected(): AbstractSelectConfig<any>[] {
        return this.unselectMultiple(...this._selectedTargets);
    }

    /**
     * 获取可选择对象索引
     * @param target 目标对象
     * @param targets 所在的对象集合
     * @param configs 配置集合
     * @returns 索引
     */
    private static getTargetIndex(target: any, targets: any[], configs: AbstractSelectConfig<any>[]): number {
        let idx = -1;
        for (let i = 0; i < targets.length; i++) {
            const cnf = configs[i];
            if (!cnf.test(target)) continue;
            const tar = targets[i];
            if (!cnf.compare(target, tar)) continue;
            idx = i;
            break;
        }
        return idx;
    }

}


export { Selector, SELECT_MODE, SELECT_STYLE };
export type { ISelectorOptions };